#include "main.h"
#include "scc.h"
#include "includes.h"

//#define USE_IRQ
#define USE_LWIP
#define USE_LIBHTTPD

#ifdef USE_LWIP
#include "lwip/debug.h"
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/tcpip.h"
#include "lwip/api.h"
#include "netif/etharp.h"
#include "netif/loopif.h"
#include "netif/ethif_cs8900a.h"
#include "arch/sys_arch.h"
#ifdef USE_LIBHTTPD
#include "httpd.h"
#endif
#endif

LOADER_STATUS	status;

#define NUM_TASK 3
#define STK_SIZE 100000
OS_STK	task_stk[NUM_TASK][STK_SIZE];
#define PRIO_INIT	10
#define PRIO_TASK1	11
#define PRIO_TASK2	12

#define PRINT print
#define printf print

#define ICMR 	(*((volatile unsigned long *)(0x40d00004))) 
#define ICLR 	(*((volatile unsigned long *)(0x40d00008))) 
#define ICIP 	(*((volatile unsigned long *)(0x40d00000))) 
#define ICPR 	(*((volatile unsigned long *)(0x40d00010))) 

#define GPDR0 	(*((volatile unsigned long *)(0x40e0000c))) 
#define GAFR0_L (*((volatile unsigned long *)(0x40e00054))) 
#define GRER0	(*((volatile unsigned long *)(0x40e00030))) 
#define GEDR0	(*((volatile unsigned long *)(0x40e00048))) 

//#define INIT_ICMR	0xfc000100
#define INIT_ICMR	0x20000100
#define INIT_ICLR	0x83000000

#if 1
//5ms
#define TIMER_INTERVAL  0x00004800

//10ms
//#define TIMER_INTERVAL  0x00009000

//100 ms
//#define TIMER_INTERVAL  0x0005A000
#else
//1 second
#define TIMER_INTERVAL	0x00384000
#endif

#define OSMR3 (*((volatile unsigned long *)(0x40a0000c))) 
#define OSCR (*((volatile unsigned long *)(0x40a00010))) 
#define OSSR (*((volatile unsigned long *)(0x40a00014))) 
#define OIER (*((volatile unsigned long *)(0x40a0001c))) 

#define NR_IRQ 32
void init_system(void *p);
void task1(void *p);
void task2(void *p);
void install_irqhandler(void * isr);
unsigned long irqactions[NR_IRQ];
int request_irq(int irqnum,void* action);
void irqaction_timetick(void);
void cs8900_irq_handler(void);


#ifdef USE_LWIP
struct netif loop_netif;
struct netif eth_cs8900a;
void tcpip_init_done(void *arg);
void tcpecho_init(void);
void libhttpd_init(void);
void libhttpd_thread(void);
#define TCPECHO_PRIO 6
#define LIBHTTPD_PRIO 6

err_t cs8900if_init(struct netif* netif);
void cs8900_service(struct netif *netif);
#endif

void c_main(){
 
	status.terminalSpeed = SERIAL_SPEED;
	status.downloadSpeed = SERIAL_DOWNLOAD_SPEED;
	SerialInit(status.terminalSpeed);

	int i;
	print("c_main - &i = %x\n",&i);

	/*
	extern unsigned long _ld_text_start;
	extern unsigned long _ld_text_and_data_size;
	print("text_start = %d\n",_ld_text_start);
	print("text_data_size = %d\n",_ld_text_and_data_size);
	*/

	request_irq(29,irqaction_timetick);
#ifdef USE_LWIP
	//request_irq(8,cs8900_irq_handler);
	#ifdef USE_IRQ
	request_irq(8,cs8900_service);
	#endif
#endif
	//install_irqhandler(OS_CPU_IRQ_ISR);
	//print("c_main = %x\n",c_main);

	#if 1
	OSInit();

	OSTaskCreate(init_system,NULL,&task_stk[0][STK_SIZE-1],PRIO_INIT);
#ifdef USE_LWIP
	OSTaskCreate(task1,NULL,&task_stk[1][STK_SIZE-1],PRIO_TASK1);
#endif
	OSTaskCreate(task2,NULL,&task_stk[2][STK_SIZE-1],PRIO_TASK2);

	OSStart();
	#endif
}


void init_system(void *p) {

	unsigned long vOSCR;
	unsigned long vOSSR;
	unsigned long vICMR;
#ifdef USE_LWIP
	sys_sem_t sem;
#endif

	int i;
	//print("init_system - &i = %x\n",&i);

	/*init OS Timer*/
#if 0
	OSCR = 0x0;
	vOSCR = OSCR;
	vOSCR += TIMER_INTERVAL;
	OSMR3 = vOSCR;
	
	OIER = 0x08;
	vOSSR = OSSR;
	OSSR = vOSSR;
#else
	OSCR = 0x0;
	OSCR = 0x0;
	OSCR = 0x0;
	OSMR3 = TIMER_INTERVAL;
	OIER = 0x08;
	vOSSR = OSSR;
	OSSR = vOSSR;
#endif 

#if 1
   /*
	*init Ethernet interrupt
	*/
	//set nCS1,that is GPIO15	
	GPDR0 |= (1<<15);
	GAFR0_L |= (2<<30);

	//set GPIO0 as input
 	GPDR0 &= ~(1<<0);
	GAFR0_L &= ~(3<<0);
	
#ifdef USE_IRQ
	//GPIO0 - rising edge detect
	GRER0 |= (1<<0);

	ICMR |= (1<<8);
	ICLR &= ~(1<<8);
#endif
#endif

	/*enable all interrupts here*/
	ICMR = INIT_ICMR;

	vICMR = ICMR;
	print("in init_system: ICMR = %x\n",vICMR);
	print("init_system done\n");

	/*
	i = 0;
	while(1) {
		//print("in init_system loop,i = %d\n",i++);
		OSTimeDly(1000);
	}
	*/

	//sem = sys_sem_new(0);
	//sys_sem_wait(sem);

	OSTaskDel(PRIO_INIT);
}

#ifdef USE_LWIP
void task1(void *p) {
	
	struct ip_addr ipaddr, netmask, gw;
	sys_sem_t sem_task1;
	sys_sem_t sem;
	struct netif* tmp_if;

	print("in task1\n");
  	
#if 0
	#ifdef STATS
  	stats_init();
	#endif /* STATS */
 	sys_init();
  	mem_init();
  	memp_init();
  	pbuf_init();
	//raw_init();

  	print("System initialized.\n");
	//mainthread
  	netif_init();
	print("after netif_init\n");
#endif

#if 1
  	sem = sys_sem_new(0);
  	tcpip_init(tcpip_init_done, &sem);
	print("after tcpip_init\n");
	print("OSPrioCur = %d\n",OSPrioCur);
	
  	sys_sem_wait(sem);
	print("after sys_sem_wait\n");
  	sys_sem_free(sem);
	print("after sys_sem_free\n");
#else
	tcpip_init(NULL,NULL);
	print("after tcpip_init\n");
#endif

  	print("TCP/IP initialized.\n");
  
  	//add loop interface
  	IP4_ADDR(&gw, 127,0,0,1);
  	IP4_ADDR(&ipaddr, 127,0,0,1);
  	IP4_ADDR(&netmask, 255,0,0,0);

	loop_netif.name[0] = 'l';
	loop_netif.name[1] = 'o';
  
 	netif_add(&loop_netif,&ipaddr, &netmask, &gw,NULL, loopif_init, tcpip_input);
	print("after netif_add loop_netif\n");
	    
  	//add ne2k interface
	/*
  	IP4_ADDR(&gw, 10,0,0,1);
  	IP4_ADDR(&ipaddr, 10,0,0,2);
  	//IP4_ADDR(&netmask, 255,255,255,0);
  	IP4_ADDR(&netmask, 255,0,0,0);
	*/
	IP4_ADDR(&gw,192,168,51,254);
	IP4_ADDR(&ipaddr,192,168,51,200);
	IP4_ADDR(&netmask,255,255,255,0);
  	
	//cs8900if_init
	eth_cs8900a.name[0] = 'e';
	eth_cs8900a.name[1] = 't';
	tmp_if = netif_add(&eth_cs8900a,&ipaddr, &netmask, &gw,NULL, cs8900if_init, tcpip_input);
  	netif_set_default(tmp_if);
  
  	print("after netif_add cs8900a\n");
 	// conn = netconn_new(NETCONN_TCP);
  	//netconn_connect(conn,&gw,80);

  	print("Applications started.\n");
  
	//we can add tcpip application here !
	//yangye 2003-1-27
	//notice :must use sys_thread_new() to add lwip tasks !!!
	//OSTaskCreate(udpecho_thread, (void *)0x1111, &LwipStk1[TASK_STK_SIZE - 1], 6);
	//  httpd_init();


	//OSTaskCreate(task2,NULL,&task_stk[2][STK_SIZE-1],PRIO_TASK2);

#ifdef USE_LIBHTTPD //libhttpd
	libhttpd_init();
#else //tcp echo
  	/*
  	str = malloc(10);
  	if(str != NULL) {
  		strcpy(str,"123");
  		skyeye_print("%s\n",str);
  		free(str);
  	}
  	*/
  	tcpecho_init();
  
  	//rtlif = rtl8019if_netif->state; //test!!!!!  

  	/* Block for ever. */
#if 1
  	sem_task1 = sys_sem_new(0);
  	//print("tcpecho created!\n");
	//print("after sys_sem_new,sem = %x\n",sem_task1);
	//print("OSPrioCur = %d\n",OSPrioCur);
  	sys_sem_wait(sem_task1);
  	print(" never goes here, should not appear!\n");
#endif
#endif

	OSTaskDel(PRIO_TASK1);
}
#endif

void task2(void *p) {
#if 1
	int i = 0;

	print("in task2 - 1\n");
	OSTimeDly(1);
	print("in task2 - 2\n");
	OSTimeDly(1);
	print("in task2 - 3\n");
	OSTimeDly(1);

	#ifndef USE_IRQ
	while(1) {
		//print("in task2 loop,i = %d\n",i++);
		cs8900_service(&eth_cs8900a);
		//OSTimeDly(10);
	}
	#endif
#else
	struct netbuf* buf;
	char str[] = "hello";

	buf = netbuf_new();

	if(buf == NULL) {
		print("error netbuf_new\n");
	}
	netbuf_alloc(buf,100);

	print("%d\n",netbuf_len(buf));
	netbuf_ref(buf,str,sizeof(str));
	print("%d\n",netbuf_len(buf));

//	netbuf_free(buf);
	netbuf_delete(buf);

#endif

	OSTaskDel(PRIO_TASK2);
}

void irqaction_timetick(void) {
	unsigned long vOIER;
	unsigned long vOSSR;
	unsigned long vOSCR;
	unsigned long vOSMR3;
	unsigned long vRTSR;
	unsigned long vRTTR;
	unsigned long tmp;

	//print("in irqaction_timetick\n");

#if 0
	vOSSR = OSSR;
	//print("OSSR = %x\n",vOSSR);
	OSSR = 0xf;

	vOSCR = OSCR;
	tmp = vOSCR;
	vOSCR += TIMER_INTERVAL;
	OSMR3 = vOSCR;
	print("OSCR = %x\tOSMR3=%x\n",tmp,vOSCR);
#else
	vOSSR = OSSR;
	//print("OSSR = %x\n",vOSSR);
	OSSR = 0xf;
	OSCR = 0x0;
	OSCR = 0x0;
	OSCR = 0x0;

	//print("OSCR = %x\tOSMR3=%x\n",tmp,vOSCR);
#endif

	OSTimeTick();
	//print("OSTimeTick\n");
}

void OS_CPU_IRQ_ISR_Handler(void) {
	unsigned long vICMR;
	unsigned long vICPR;
	unsigned long vICIP;
	unsigned long tmp;
	int i;
	void (*isr)(void);

#if OS_CRITICAL_METHOD == 3
	OS_CPU_SR cpu_sr;
#endif

	OS_ENTER_CRITICAL();
#if 0
	vOIER = REG_OIER;
	vOIER &= 0x7;
	REG_OIER = vOIER;
#endif
#if 1

	
	vICMR = ICMR;
	vICIP = ICIP;
	vICIP &= vICMR;
//#define DEBUG_IRQ
#undef DEBUG_IRQ
#ifdef DEBUG_IRQ
	print("__irq before - ICIP = %x\t",vICIP);
	print("ICMR = %x\n",vICMR);
#endif

	//ICMR &= ~(1<<8);
	for(i = 0, tmp = 0x1; i < NR_IRQ; ++i,tmp <<= 1) {
		if((vICIP & tmp) && (irqactions[i] != (unsigned long)NULL)) {
			isr = irqactions[i];
			if(tmp == 0x00000100) {
				cs8900_service(&eth_cs8900a);
				GEDR0 = GEDR0;
				#if 0
				GAFR0_L |= (2<<30);

				//set GPIO0 as input
 				GPDR0 &= ~(1<<0);
				GAFR0_L &= ~(3<<0);
	
				//GPIO0 - rising edge detect
				GRER0 |= (1<<0);

				ICMR |= (1<<8);
				ICLR &= ~(1<<8);
				#endif
			}
			else
				isr();
		}
	}
	//ICMR |= (1<<8);
	//ICIP = 0x0;

	vICMR = ICMR;
	vICIP = ICIP;
	vICIP &= vICMR;
#ifdef DEBUG_IRQ
	print("__irq after - ICIP = %x\t",vICIP);
	print("ICMR = %x\n",vICMR);
#endif
#endif

	//print("in irq handler\n");
	OS_EXIT_CRITICAL();

}

void OS_CPU_FIQ_ISR_Handler(void) {

}

#ifdef USE_LWIP
void tcpip_init_done(void *arg)
{
  sys_sem_t *sem;
  sem = arg;
  sys_sem_signal(*sem);
}

static void 
tcpecho_thread(void *arg)
{
  struct netconn *conn, *newconn;
  struct ip_addr deb;

  err_t err;
 /*
 void* ptr;

 ptr = mem_malloc(100);
 snprint(ptr,100,"helloworld\n");
 PRINT("%s",ptr);
 mem_free(ptr);
 */

  /* Create a new connection identifier. */
  conn = netconn_new(NETCONN_TCP);

  /* Bind connection to well known port number 7. */
  //netconn_bind(conn, NULL, htons(7));
  netconn_bind(conn, NULL, 7);

  /* Tell connection to go into listening mode. */
  netconn_listen(conn);

  while(1) {

	/* Grab new connection. */
	print("netconn_accept - 1\n");
	newconn = netconn_accept(conn);
	print("netconn_accept - 2\n");
	/*print("accepted new connection %p\n", newconn);*/
	/* Process the new connection. */
	if(newconn != NULL) {
	  struct netbuf *buf;
	  void *data;
	  u16_t len;
	  
	  while((buf = netconn_recv(newconn)) != NULL) {
	/*print("Recved\n");*/
	do {
	  netbuf_data(buf, &data, &len);
	  err = netconn_write(newconn, data, len, NETCONN_COPY);
	  if(err != ERR_OK) {
		/*		print("tcpecho: netconn_write: error \"%s\"\n", lwip_strerr(err));*/
	  }
	} while(netbuf_next(buf) >= 0);
	netbuf_delete(buf); 
	  }
	  /*print("Got EOF, looping\n");*/
	  /* Close connection and discard connection identifier. */
	  netconn_delete(newconn);
	}
  }
}
/*-----------------------------------------------------------------------------------*/

static void simple_ws(void);

void
tcpecho_init(void)
{
  //sys_thread_new(tcpecho_thread, NULL,TCPECHO_PRIO);  
  sys_thread_new("simple ws",simple_ws,NULL,0,TCPECHO_PRIO);
}
	
void libhttpd_init(void) {
	sys_thread_new("libhttpd",libhttpd_thread,NULL,0,LIBHTTPD_PRIO);
}

void libhttpd_thread(void) {
  	print("\n\nbefore main_httpd\n\n");
  	main_httpd("192.168.51.200",80);
  	//main_httpd(HTTP_ANY_ADDR,80);
  	print("\n\nafter main_httpd\n\n");
}

const static char indexdata[] = "<h1>Hello From LwIP</h1>";
const static char http_html_hdr[] = "HTTP/1.0 200 OK\r\n" \
									"Content-Type: text/html\r\n" \
									"Content-Length: 24\r\n\r\n";

/*
const static char http_html_hdr[] = "HTTP/1.0 200 OK\r\n" \
									"Content-Type: text/html\r\n" \
									"\r\n";
									*/

static void process_connection(struct netconn* conn) {
	struct netbuf* inbuf;
	char* rq;
	int len;

	//inbuf = netconn_recv(conn);
	//netbuf_data(inbuf,(void**)&rq,(u16_t*)&len);
	//if(rq[0] == 'G') {
	#if 1
	netconn_write(conn,http_html_hdr,sizeof(http_html_hdr),NETCONN_NOCOPY);
	netconn_write(conn,indexdata,sizeof(indexdata),NETCONN_NOCOPY);
	netconn_close(conn);

	#else
	
	//netconn_write(conn,http_html_hdr,sizeof(http_html_hdr),NETCONN_NOCOPY);
	netconn_write(conn,http_html_hdr,strlen(http_html_hdr),NETCONN_NOCOPY);
	while(1) {
		netconn_write(conn,"12345<br>",9,NETCONN_NOCOPY);
	}
	
	#endif
	//}

}

void simple_ws(void) {
	struct netconn* conn,*newconn;

	print("in simple_ws\n");
	conn = netconn_new(NETCONN_TCP);
	netconn_bind(conn,NULL,80);
	netconn_listen(conn);

	while(1) {
		newconn = netconn_accept(conn);
		process_connection(newconn);
		netconn_delete(newconn);
	}

}

#endif




void install_irqhandler(void * isr)  {   

    /* ARM irq exception vector addr is 0x00000018  */
    unsigned int * irq_vec_addr = ( unsigned int * ) 0x18;
    /* this is isr entry address, could be another address like 0x3c, 0x58... */
    unsigned int * isr_entry_addr = ( unsigned int * ) 0x38;                    
    
    unsigned int instruction;                                                   
    
    /* set the ISR entry at 0x38  */
    *isr_entry_addr = (unsigned int)isr;                                        
    
    /* make an instruction: it is machine-code for "ldr  pc, [pc, #(38-18-8)]"  */
    instruction = ((unsigned int) isr_entry_addr  - (unsigned int)irq_vec_addr - 0x08) | 0xe59ff000;
    
    /* set this instruction at 0x18  */                                         
    *irq_vec_addr = instruction;    
}

int request_irq(int irqnum,void* action) {
	if(irqnum >= 0 && irqnum < NR_IRQ) {
		irqactions[irqnum] = (unsigned long)action;
		return 0;
	}
	return -1;
}


void Handler_undef(void) {
	print("+++undef+++\n");
	while(1);
}
void Handler_swi(void) {
	print("+++swi+++\n");
	while(1);
}
void Handler_prefetch(void) {
	print("+++prefetch+++\n");
	while(1);
}
void Handler_abort(void) {
	print("+++abort+++\n");
	while(1);
}
void Handler_fiq(void) {
	print("+++fiq+++\n");
	while(1);
}
